//MIT License
//
//Copyright(c) 2020 Goodman Tao
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files(the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions :
//
//The above copyright notice and this permission notice shall be included in all
//copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
//SOFTWARE.


#ifdef _MSC_VER
    #define _LB_2_MULTITHREAD_API __declspec(dllexport)
#endif // _MSC_VER

#ifdef __GNUC__
    #define _LB_2_MULTITHREAD_API
#endif // __GNUC__


#include "_LB_2_Multithread.h"

#include <stdlib.h>
#include <string.h>
#include "tinycthread.h"


typedef struct
{
    thrd_t *th;
}Fld;

static void Clear(void *pParams)
{
    Thread *pThis = pParams;
    Fld *pFld = pThis->Fld;

    free(pFld->th);
}

static void Create(void *_pThis, va_list *pvlArgs)
{
    Thread *pThis = _pThis;

    ThreadStart start = va_arg(*pvlArgs, ThreadStart);
    void *arg = va_arg(*pvlArgs, void *);
    ConcurrenceReturnValue *pRetVal = va_arg(*pvlArgs, void *);

    Fld *pFld = pThis->Fld;

    pFld->th = malloc(sizeof(thrd_t));
    ConcurrenceReturnValue ret = thrd_create(pFld->th, start, arg);
    if (pRetVal)
    {
        *pRetVal = ret;
    }
}

static void Detach(void *_pThis, va_list *pvlArgs)
{
    Thread *pThis = _pThis;

    ConcurrenceReturnValue *pRetVal = va_arg(*pvlArgs, void *);

    Fld *pFld = pThis->Fld;

    ConcurrenceReturnValue ret = thrd_detach(*pFld->th);
    if (pRetVal)
    {
        *pRetVal = ret;
    }
}

static void Equal(void *_pThis, va_list *pvlArgs)
{
    Thread *pThis = _pThis;

    Thread *pTh = va_arg(*pvlArgs, Thread *);
    ConcurrenceReturnValue *pRetVal = va_arg(*pvlArgs, void *);

    Fld *pFld = pThis->Fld;
    Fld *pFldIn = pTh->Fld;
    ConcurrenceReturnValue ret = thrd_equal(*pFld->th, *pFldIn->th);
    if (pRetVal)
    {
        *pRetVal = ret;
    }
}

static void Join(void *_pThis, va_list *pvlArgs)
{
    Thread *pThis = _pThis;

    int *res = va_arg(*pvlArgs, int *);
    ConcurrenceReturnValue *pRetVal = va_arg(*pvlArgs, void *);

    Fld *pFld = pThis->Fld;
    ConcurrenceReturnValue ret = thrd_join(*pFld->th, res);
    if (pRetVal)
    {
        *pRetVal = ret;
    }
}

__CONSTRUCTOR(Thread)
{
    return __Inst(__TYPE(Thread), sizeof(Fld), Clear, 4, 0,
        __METHOD(Create),
        __METHOD(Detach),
        __METHOD(Equal),
        __METHOD(Join));
}

/******** static part *************************************************************/
static Thread *Current()
{
    Thread *pTh = __NEW(Thread);
    Fld *pFld = pTh->Fld;
    pFld->th = malloc(sizeof(thrd_t));

    thrd_t tmp = thrd_current();
    memcpy(pFld->th, &tmp, sizeof(thrd_t));

    return pTh;
}

static int __Sleep(const struct timespec *duration, struct timespec *remaining)
{
    return thrd_sleep(duration, remaining);
}

static void __Exit(int retCode)
{
    thrd_exit(retCode);
}

static void _Yield()
{
    thrd_yield();
}

__STATICASSIGN(Thread, Current, __Sleep, __Exit, _Yield);
